目前網站在瀏覽器的分頁名稱上,要嘛直接顯示路徑,要嘛就是只顯示文章的標題。對網站的識別性來說,這兩者的體驗都是不好的,今天就來實作讓普通頁面預設是網站名稱,如果是文章,則是後綴網站名稱,讓分頁更具識別性。
在實作時,要先了解一個 Nuxt Content 關於 title 的機制,這部分是我自己測試出來的,實際行為請以 Nuxt Content 的 source code 為主:
app.head.title
時,第一次進入網站頁面會以該值為 <Title />
。<ContentDoc />
的頁面中,若對應的內容檔案在 Frontmatter 有設定 title 時,會更新當前 <Title />
為 title。<ContentDoc />
的頁面中,若對應的內容檔案在 Frontmatter 沒有設定 title
時,系統會用檔案名稱作為 <Title />
,且已經會將每字首位字母大寫、並將連字號轉成空白的。<ContentDoc />
,不會更新 <Title />
,就算 <Title />
已被改成其他內容檔案的名稱,也不會改回 app.head.title
的值。index.md
若對應的內容檔案在 Frontmatter 沒有設定 title
時,不會更新 <Title />
。若要用虛擬程式碼去解釋這個規則,大概是長得像這樣:
const main = () => {
config = useConfig()
head = useHead()
head.title = config.title
changePageHooks.add((page) => {
if (!page.contains('<ContentDoc />')) {
return
}
if (!page.content.filename === 'index.md') {
return
}
if (page.content.frontmatter.title) {
head.title = page.contentfrontmatter.title
} else {
head.title = page.content.filename.toTitleCase()
}
})
}
所以就會發生一個情況是,當我切換到某篇文章頁面,分頁標題更新後,如果到了其他不會更新分頁標題的頁面,就會變得牛頭不對馬尾的情況。
為了應對這個狀況,我新增了一個 useGarden()
的 composable 來處理這個狀態,只要頁面或主要元件有使用就能顯示我期望的標題。
新增 @/composables/useGarden.ts
:
import { Post } from "@/types"
import { startCase } from "@/libraries/formater"
function getTitleByRouteSlug() {
const route = useRoute()
const slug = route.name?.toString() || ''
return startCase(slug)
}
export function useGarden(post : Post|undefined = undefined ) {
const title = post?.title || getTitleByRouteSlug() || ''
const separator = title ? ' - ' : ''
const headTitle = computed(() => `${title}${separator}My Digital Garden`)
if (post) {
post.head = post.head || {}
post.head.title = post.head.title || headTitle
}
useHead({title: headTitle})
}
這個 composable 會先決定標題,若有文章且有 title 屬性,則直接沿用;若無則使用其 slug 去轉換。最後在這個標題後面加上網站名稱。
接著我在 @/index.vue
、@/components/List/Page.vue
、@/components/Post/Page.vue
、@/pages/tag/index.vue
、@/pages/tag/index.vue
加上 useGarden()
就可以覆蓋多數情況了。